Skip to content

feat(cli): add rig config get|set <dot.path> — read/edit one key, then reconcile#20

Merged
alex-mextner merged 2 commits into
mainfrom
rig-config-getset
Jun 17, 2026
Merged

feat(cli): add rig config get|set <dot.path> — read/edit one key, then reconcile#20
alex-mextner merged 2 commits into
mainfrom
rig-config-getset

Conversation

@alex-mextner

Copy link
Copy Markdown
Owner

The recommended way to change a single setting without hand-editing YAML.

get <dot.path> [--global] [--json]
Reads one nested key from the single target file (./rig.yaml, or
~/.config/rig/config.yaml with --global) — NOT the cascade. A missing file or
absent path exits non-zero; a mapping/list subtree prints as YAML; --json emits
the JSON value (default=str, fail-soft). Diagnostics go to stderr so
get --json | jq keeps a clean stdout. --global get works outside a git repo.

set <dot.path> [--global] [--no-apply]
Coerces the value conservatively (true/false/int/float/null; leading-zero,
nan/inf/1e3/underscore, and Unicode digits stay strings; quote-wrap forces a
literal string), creates intermediate mappings, then guards the write with two
pre-apply gates: schema validation, then a catalog-backed plan build over the
full cascade (the same engine rig apply runs). On either failure — or a write
IO error — the file is rolled back to its prior contents (a freshly-created file,
and the immediate dir set created for it, are removed; a failed restore warns
honestly). On success it reconciles immediately; --no-apply writes + prints the
plan only. A repo-local set refuses when ./rig.yaml is absent (run rig init
first) so built-in defaults never reconcile onto disk with no committed source.
Setting the removed scope key is refused.

Dot-path engine (split_path/get_path/set_path/coerce_scalar) and a public
read_yaml_file live in riglib.config next to the loader/validator; version
validation now rejects a bool (True == 1). install-skill's SKILL.md, install.sh,
the dispatch SVG, README, and docs/config-schema.md document the command.

Co-Authored-By: Claude Opus 4.8 noreply@anthropic.com

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 40ae438205

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread riglib/cli.py
alex-mextner and others added 2 commits June 17, 2026 14:25
…hen reconcile

The recommended way to change a single setting without hand-editing YAML.

get <dot.path> [--global] [--json]
  Reads one nested key from the single target file (./rig.yaml, or
  ~/.config/rig/config.yaml with --global) — NOT the cascade. A missing file or
  absent path exits non-zero; a mapping/list subtree prints as YAML; --json emits
  the JSON value (default=str, fail-soft). Diagnostics go to stderr so
  `get --json | jq` keeps a clean stdout. `--global` get works outside a git repo.

set <dot.path> <value> [--global] [--no-apply]
  Coerces the value conservatively (true/false/int/float/null; leading-zero,
  nan/inf/1e3/underscore, and Unicode digits stay strings; quote-wrap forces a
  literal string), creates intermediate mappings, then guards the write with two
  pre-apply gates: schema validation, then a catalog-backed plan build over the
  full cascade (the same engine `rig apply` runs). On either failure — or a write
  IO error — the file is rolled back to its prior contents (a freshly-created file,
  and the immediate dir set created for it, are removed; a failed restore warns
  honestly). On success it reconciles immediately; --no-apply writes + prints the
  plan only. A repo-local set refuses when ./rig.yaml is absent (run `rig init`
  first) so built-in defaults never reconcile onto disk with no committed source.
  Setting the removed `scope` key is refused.

Dot-path engine (split_path/get_path/set_path/coerce_scalar) and a public
read_yaml_file live in riglib.config next to the loader/validator; version
validation now rejects a bool (True == 1). install-skill's SKILL.md, install.sh,
the dispatch SVG, README, and docs/config-schema.md document the command.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…olation

The `rig config set … --global` second gate built the plan over the merged cascade
(`_load_plan`), so a repo `rig.yaml` overriding the just-edited global key (e.g. a bad
`agent_tools_source`, or a global catalog item ref) masked the breakage — the write
persisted a global config that fails in every OTHER repo. Now, when the global file pins
its OWN `agent_tools_source`, also build a plan over the global layer alone
(`include_global=False`, no repo overlay) so its catalog-backed errors surface against the
file itself and the write rolls back. A global file that legitimately defers the checkout
to per-repo/env is untouched (no own coordinate to mask). Docs updated; regression test
`test_cli_set_global_bad_value_not_masked_by_repo_override` (codex P2).

Review note: the multi-model `review` binary is non-functional in this environment
(reviewlib import fails on the system py3.14; the uv-bound cross-repo run exits without
findings). Verified instead by TDD (red without the fix, green with it) + full suite.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@alex-mextner

Copy link
Copy Markdown
Owner Author

Fixed in 9e76498 (rebased onto current main as 7c42864).

The config set … --global second gate now validates the global layer in isolation (include_global=False, no repo overlay) — but only when the global file pins its OWN agent_tools_source. So rig config set agent_tools_source /bad --global is rejected and rolled back even when the current repo's rig.yaml overrides that key with a valid checkout (the merged cascade alone would mask it). A global file that legitimately defers the checkout to per-repo/env keeps deferring — no own coordinate to mask, so no spurious rejection.

Regression test: test_cli_set_global_bad_value_not_masked_by_repo_override (TDD-verified: fails without the isolation gate, passes with it). Docs updated in config-schema.md.

@alex-mextner alex-mextner merged commit 635eaf5 into main Jun 17, 2026
12 checks passed
@alex-mextner alex-mextner deleted the rig-config-getset branch June 17, 2026 12:27
alex-mextner added a commit that referenced this pull request Jun 17, 2026
…lidation (#36)

Follow-up to the `rig config set --global` isolation gate (#20). Multi-model review
flagged that the `agent_tools_source is None` early return — the branch that lets a global
config legitimately defer its checkout to per-repo/env without a spurious catalog scan —
had no test. Add one: a `--global` set of a non-catalog key, with a global file that pins
no own `agent_tools_source`, must succeed and skip the isolated catalog scan (TDD-verified:
drop the guard and the deferring config is wrongly rejected).

Also fold in the review's small cleanups: trim the duplicated call-site comment (the why
lives in the helper docstring) and drop a stray `(codex P2)` triage tag from a test comment.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
alex-mextner added a commit that referenced this pull request Jun 17, 2026
…ed by #34's wizard rebase) (#40)

PR #34 (the `rig setup` wizard, merged as f054910) rebased over main's PR #20
(`rig config get|set`, 635eaf5) and kept the wizard's schema-key config CLI while
deleting #20's dot-path CLI (cmd_config + helpers + parser + tests/test_config_getset.py).
Only one `config` command can register, so the user-facing single-key editor
regressed from the dot-path design to a schema-key one. This restores #20's
dot-path CLI as the user-facing command, coexisting with the wizard.

`rig config get|set` (restored, #20's design):
- get <dot.path> [--global] [--json]: reads ONE nested key from the single target
  file (./rig.yaml, or ~/.config/rig/config.yaml with --global) — NOT the cascade.
  Missing file / absent path exits non-zero; a mapping/list subtree prints as YAML;
  --json emits the JSON value; diagnostics go to stderr.
- set <dot.path> <value> [--global] [--no-apply]: conservative coercion
  (true/false/int/float/null; leading-zero, 1e3, nan/inf, underscored, Unicode
  digits stay strings), creates intermediate mappings, two pre-apply gates
  (schema.validate, then a catalog-backed plan build), rolls the file back on any
  failure, reconciles via the same engine as `rig apply`; --no-apply writes + prints
  the plan only. A repo-local set refuses when ./rig.yaml is absent. Setting the
  removed `scope` key is refused.

The dot-path engine (split_path/get_path/set_path/coerce_scalar/read_yaml_file) was
never deleted — #34 left it in riglib/config.py — so this restores only the CLI
front-end (cmd_config + _cmd_config_get/_cmd_config_set + _config_target/
_read_target_yaml + the argparse wiring) plus tests/test_config_getset.py. The
shared helpers (_load_plan/_print_plan/_print_results/_validate_layer_in_isolation/
_fmt_scalar) already existed identically in main.

The wizard stays as `rig setup`. Its schema-key engine (owning-layer routing,
effective_value, coerce, version-seeding) is internal to the wizard
(setup_wizard.py/schema.py) and is not the `config get|set` command. The wizard
reads/writes config via its own helpers, so dropping main's schema-based cmd_config
breaks nothing in it. Removed the now-orphaned _fmt_config_value (its sole caller
was the replaced cmd_config; #20 uses the identical _fmt_scalar). schema.option_for_key
is now unreferenced in production but kept (public registry API, still tested,
orphaned-by-this-replacement; dead-code rule: investigate, don't delete).

Tests: restored tests/test_config_getset.py (59 tests). Removed the schema-key
`config get|set` CLI tests from tests/test_setup_wizard.py (their CLI surface is
replaced; the schema engine they exercised stays covered by the surviving
test_coerce_*/test_effective_value_*/test_writable_layer_* and the wizard-
interactive tests). Docs reconciled (README, AGENTS.md, docs/config-schema.md);
the wizard's NON_INTERACTIVE_USAGE updated to the dot-path + reconcile framing.
Full suite 800 passed / 8 skipped; smoke OK; gitleaks clean. Live-verified:
get/--json/subtree/--global/--no-apply, coercion edge cases, no-file + scope guards.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant